package com.express.gateway.manage.entity;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

import com.express.gateway.common.util.DateTimeUtils;
import com.express.gateway.common.util.StrUtil;
import com.express.gateway.manage.enums.HttpMethodEnum;
import com.express.gateway.manage.enums.TimeUnitEnum;
import com.express.gateway.spi.auth.AuthOptions;
import com.express.gateway.spi.round.AfterHandlerOptions;
import com.express.gateway.spi.round.BeforeHandlerOptions;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

/**
 * API类
 * 
 * @author flower
 *
 */
public class Api {
	/** 应用网关的名字 */
	private String appName;
	/** API的名字 */
	private String apiName;
	/** API的描述 */
	private String apiDescribe;
	/** API的创建时间 */
	private Instant apiCreateTime;
	/** 访问限制单位 */
	private TimeUnitEnum limitUnit;
	/** API访问限制次数 */
	private long apiLimit;
	/** IP访问限制次数 */
	private long ipLimit;
	/** 是否透传body */
	private boolean passBody;
	/** 是否将body的参数映射到query允许被query访问 */
	private boolean bodyAsQuery = true;
	/** 接口路径 */
	private String path;
	/** 权限配置信息 */
	private AuthOptions authOptions;
	/** 前置处理器配置信息 */
	private BeforeHandlerOptions beforeHandlerOptions;
	/** 后置处理器配置信息 */
	private AfterHandlerOptions afterHandlerOptions;
	/** 返回的Content-Type类型 */
	private String contentType;
	/** 接口consumes */
	private Set<String> consumes;
	/** 请求方法类型 */
	private HttpMethodEnum method;
	/** 参数的配置 */
	private List<EntranceParam> enterParam;
	/** API 服务端入口 */
	private ServerEntrance serverEntrance;

	/** API 接受的文件类型喝大小 */
	private Map<String, Long> uploadParam;

	/** API返回结果 */
	private Result result;

	public Api() {
		super();
	}

	/**
	 * 将对象转换为JsonObject
	 *
	 * @return
	 */
	public JsonObject toJson() {
		JsonObject json = new JsonObject();
		json.put("appName", this.appName);
		json.put("apiName", this.apiName);
		json.put("apiDescribe", this.apiDescribe);
		json.put("apiCreateTime", DateTimeUtils.DATE_TIME.formatDate(this.apiCreateTime));
		json.put("path", this.path);
		json.put("apiLimit", this.apiLimit);
		json.put("ipLimit", this.ipLimit);
		json.put("passBody", this.passBody);
		json.put("bodyAsQuery", this.bodyAsQuery);

//		if (consumes != null) {
//			JsonArray array = new JsonArray();
//			this.consumes.stream().filter(Objects::nonNull).filter(v -> !StrUtil.isNullOrEmpty(v)).forEach(array::add);
//			json.put("consumes", array);
//		}

//		if (enterParam != null) {
//			JsonArray array = new JsonArray();
//			this.enterParam.forEach(va -> {
//				array.add(va.toJson());
//			});
//			json.put("enterParam", array);
//		}
		Optional.ofNullable(method).ifPresent(v -> json.put("method", v));
		Optional.ofNullable(limitUnit).ifPresent(v -> json.put("limitUnit", v));
		Optional.ofNullable(contentType).ifPresent(v -> json.put("contentType", v));

		Optional.ofNullable(authOptions).map(AuthOptions::toJson).ifPresent(v -> json.put("authOptions", v));
		Optional.ofNullable(beforeHandlerOptions).map(BeforeHandlerOptions::toJson).ifPresent(v -> json.put("beforeHandlerOptions", v));
		Optional.ofNullable(afterHandlerOptions).map(AfterHandlerOptions::toJson).ifPresent(v -> json.put("afterHandlerOptions", v));

		Optional.ofNullable(serverEntrance).map(ServerEntrance::toJson).ifPresent(v -> json.put("serverEntrance", v));
		Optional.ofNullable(result).map(Result::toJson).ifPresent(v -> json.put("result", v));

		Optional.ofNullable(consumes).map(d -> d.stream().filter(Objects::nonNull).filter(v -> !StrUtil.isNullOrEmpty(v)).collect(Collectors.toList())).ifPresent(v -> json.put("consumes", v));
		Optional.ofNullable(enterParam).map(d -> d.stream().map(EntranceParam::toJson).collect(Collectors.toList())).ifPresent(v -> json.put("enterParam", v));
		return json;
	}

	/**
	 * 通过JsonObject实例化一个对象
	 *
	 * @param json
	 * @return
	 */
	public static Api fromJson(JsonObject json) {
		if (json == null) {
			return null;
		}
		// json.mapTo(Api.class);
		Api option = new Api();
		if (json.getValue("appName") instanceof String) {
			option.setAppName(json.getString("appName").trim());
		}
		if (json.getValue("apiName") instanceof String) {
			option.setApiName(json.getString("apiName").trim());
		}
		if (json.getValue("apiDescribe") instanceof String) {
			option.setApiDescribe(json.getString("apiDescribe").trim());
		}
		if (json.getValue("path") instanceof String) {
			option.setPath(json.getString("path").trim());
		}
		if (json.getValue("contentType") instanceof String) {
			option.setContentType(json.getString("contentType").trim());
		}
		if (json.getValue("apiCreateTime") instanceof Instant) {
			option.setApiCreateTime(json.getInstant("apiCreateTime"));
		} else if (json.getValue("apiCreateTime") instanceof String) {
			Instant instant = DateTimeUtils.DATE_TIME.parseInstant(json.getString("apiCreateTime").trim());
			option.setApiCreateTime(instant);
		}
		if (json.getValue("limitUnit") instanceof String) {
			option.setLimitUnit(TimeUnitEnum.valueOf(json.getString("limitUnit").trim()));
		}
		if (json.getValue("apiLimit") instanceof Number) {
			option.setApiLimit(((Number) json.getValue("apiLimit")).longValue());
		}
		if (json.getValue("ipLimit") instanceof Number) {
			option.setIpLimit(((Number) json.getValue("ipLimit")).longValue());
		}
		if (json.getValue("passBody") instanceof Boolean) {
			option.setPassBody(json.getBoolean("passBody"));
		}
		if (json.getValue("bodyAsQuery") instanceof Boolean) {
			option.setBodyAsQuery(json.getBoolean("bodyAsQuery"));
		}
		if (json.getValue("method") instanceof String) {
			option.setMethod(HttpMethodEnum.valueOf(json.getString("method").trim()));
		}
		if (json.getValue("consumes") instanceof JsonArray) {
			Set<String> set = new HashSet<>();
			json.getJsonArray("consumes").forEach(va -> {
				Optional.ofNullable(va).filter(v -> v instanceof String).map(Object::toString).filter(v ->!StrUtil.isNullOrEmpty(v.trim())).ifPresent(set::add);
			});
			option.setConsumes(set);
		}
		if (json.getValue("authOptions") instanceof JsonObject) {
			option.setAuthOptions(AuthOptions.fromJson(json.getJsonObject("authOptions")));
		}
		if (json.getValue("beforeHandlerOptions") instanceof JsonObject) {
			option.setBeforeHandlerOptions(BeforeHandlerOptions.fromJson(json.getJsonObject("beforeHandlerOptions")));
		}
		if (json.getValue("afterHandlerOptions") instanceof JsonObject) {
			option.setAfterHandlerOptions(AfterHandlerOptions.fromJson(json.getJsonObject("afterHandlerOptions")));
		}
		if (json.getValue("enterParam") instanceof JsonArray) {
			List<EntranceParam> list = new ArrayList<>();
			json.getJsonArray("enterParam").forEach(va -> {
				if (va instanceof JsonObject) {
					list.add(EntranceParam.fromJson((JsonObject) va));
				}
			});
			option.setEnterParam(list);
		}
		if (json.getValue("serverEntrance") instanceof JsonObject) {
			option.setServerEntrance(ServerEntrance.fromJson(json.getJsonObject("serverEntrance")));
		}
		if (json.getValue("result") instanceof JsonObject) {
			option.setResult(Result.fromJson(json.getJsonObject("result")));
		}
		return option;
	}

	public String getAppName() {
		return appName;
	}

	public void setAppName(String appName) {
		this.appName = appName;
	}

	public String getApiName() {
		return apiName;
	}

	public void setApiName(String apiName) {
		this.apiName = apiName;
	}

	public String getApiDescribe() {
		return apiDescribe;
	}

	public void setApiDescribe(String apiDescribe) {
		this.apiDescribe = apiDescribe;
	}

	public Instant getApiCreateTime() {
		return apiCreateTime;
	}

	public void setApiCreateTime(Instant apiCreateTime) {
		this.apiCreateTime = apiCreateTime;
	}

	public TimeUnitEnum getLimitUnit() {
		return limitUnit;
	}

	public void setLimitUnit(TimeUnitEnum limitUnit) {
		this.limitUnit = limitUnit;
	}

	public long getApiLimit() {
		return apiLimit;
	}

	public long getIpLimit() {
		return ipLimit;
	}

	public boolean isPassBody() {
		return passBody;
	}

	public void setPassBody(boolean passBody) {
		this.passBody = passBody;
	}

	public boolean isBodyAsQuery() {
		return bodyAsQuery;
	}

	public void setBodyAsQuery(boolean bodyAsQuery) {
		this.bodyAsQuery = bodyAsQuery;
	}

	public AuthOptions getAuthOptions() {
		return authOptions;
	}

	public void setAuthOptions(AuthOptions authOptions) {
		this.authOptions = authOptions;
	}

	public BeforeHandlerOptions getBeforeHandlerOptions() {
		return beforeHandlerOptions;
	}

	public void setBeforeHandlerOptions(BeforeHandlerOptions beforeHandlerOptions) {
		this.beforeHandlerOptions = beforeHandlerOptions;
	}

	public AfterHandlerOptions getAfterHandlerOptions() {
		return afterHandlerOptions;
	}

	public void setAfterHandlerOptions(AfterHandlerOptions afterHandlerOptions) {
		this.afterHandlerOptions = afterHandlerOptions;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public Set<String> getConsumes() {
		return consumes;
	}

	public void setConsumes(Set<String> consumes) {
		this.consumes = consumes;
	}

	public HttpMethodEnum getMethod() {
		return method;
	}

	public void setMethod(HttpMethodEnum method) {
		this.method = method;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		// 判断是否带有/杠,如果没有就加上
		if (Objects.nonNull(path) && !path.isEmpty() && path.charAt(0) != '/') {
			this.path = "/" + this.path;
		} else {
			this.path = path;
		}
	}

	public void setApiLimit(long apiLimit) {
		this.apiLimit = apiLimit;
	}

	public void setIpLimit(long ipLimit) {
		this.ipLimit = ipLimit;
	}

	public List<EntranceParam> getEnterParam() {
		return enterParam;
	}

	public void setEnterParam(List<EntranceParam> enterParam) {
		this.enterParam = enterParam;
	}

	public ServerEntrance getServerEntrance() {
		return serverEntrance;
	}

	public void setServerEntrance(ServerEntrance serverEntrance) {
		this.serverEntrance = serverEntrance;
	}

	public Map<String, Long> getUploadParam() {
		return uploadParam;
	}

	public void setUploadParam(Map<String, Long> uploadParam) {
		this.uploadParam = uploadParam;
	}

	public Result getResult() {
		return result;
	}

	public void setResult(Result result) {
		this.result = result;
	}

	public String keyMessage() {
		return "APP:" + this.getAppName() + ", API:" + this.getApiName() + ", PATH:" + this.getPath();
	}

	@Override
	public String toString() {
		return "ExpApis [apiName=" + apiName + ", apiDescribe=" + apiDescribe + ", apiCreateTime=" + apiCreateTime + ", limitUnit=" + limitUnit
				+ ", apiLimit=" + apiLimit + ", ipLimit=" + ipLimit + ", path=" + path + ", authOptions=" + authOptions + ", beforeHandlerOptions="
				+ beforeHandlerOptions + ", afterHandlerOptions=" + afterHandlerOptions + ", contentType=" + contentType + ", consumes=" + consumes
				+ ", method=" + method + ", enterParam=" + enterParam + ", serverEntrance=" + serverEntrance + ",uploadParam=" + uploadParam
				+ ", result=" + result + "]";
	}

}
